package com.system.config.mybatis.plugins.page;
import org.apache.ibatis.executor.statement.PreparedStatementHandler;
import org.apache.ibatis.executor.statement.RoutingStatementHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Properties;

/**
 * mybatis 分页插件原理 Demo ，  动态代理 prepare 方法， 执行顺序： Executor --》 StatementHandler --》 ResultSetHandler
 * 记得在 sqlSessionFactoryBean 加入该插件
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof RoutingStatementHandler) {
            RoutingStatementHandler target = (RoutingStatementHandler) invocation.getTarget();
            // 反射获取SQL
            Field delegateField = getField(target, "delegate");
            PreparedStatementHandler statementHandler = (PreparedStatementHandler) delegateField.get(target);
            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            // 需要根据 parameterObject 进行对应的 分页计算。 此处只简单演示
            sql = sql + " LIMIT 0,10";
            Object parameterObject = boundSql.getParameterObject();
            // 设置 sql
            Field sqlField = getField(boundSql, "sql");
            sqlField.set(boundSql, sql);
        }
        return invocation.proceed();
    }

    /**
     * 反射获取值
     */
    private Field getField(Object target, String name) {
        Field delegateField = ReflectionUtils.findField(target.getClass(), name);
        delegateField.setAccessible(true);
        return delegateField;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof RoutingStatementHandler) {
            return Plugin.wrap(target, this);
        }
        return target;
    }
    @Override
    public void setProperties(Properties properties) {
    }
}
